import requests
from typing import Optional, Dict, List, Union
import docx
import time
import json
from loguru import logger
from docx.oxml.table import CT_Tbl
from docx.oxml.text.paragraph import CT_P
from docx.table import _Cell, Table
from docx.text.paragraph import Paragraph
from pathlib import Path


class GetWord:
    """Word文档处理类
    为了兼容现有应用提供的接口，与WordProcessor功能相同
    """
    
    def __init__(self):
        self.config = {
            "word_url": "",  # word文件URL
            "word_file_desc": "",  # 文件描述
            "local_word_file_path": ""  # 本地文件路径
        }
        self.processor = WordProcessor()

    def parse_a_docx_table(self, some_doc_table: Table) -> str:
        """解析Word文档中的表格为Markdown格式"""
        return self.processor.parse_a_docx_table(some_doc_table)

    def docx_iter_block_items(self, parent: Union[docx.Document, _Cell]):
        """遍历文档中的段落和表格"""
        return self.processor.docx_iter_block_items(parent)

    def get_doc_content(self, file_path: str) -> List[str]:
        """获取文档内容"""
        return self.processor.get_doc_content(file_path)

    def save_remote_file(self, file_name: str, unique_str: str) -> str:
        """保存远程文件到本地临时目录"""
        return self.processor.save_remote_file(file_name, unique_str)

    def process_document(self, word_url: str = "", local_word_file_path: str = "") -> Dict:
        """处理Word文档并返回内容"""
        return self.processor.process_document(word_url, local_word_file_path)


class WordProcessor:
    """Word文档处理器
    用于处理Word文档的输入，支持本地文件和远程URL
    """
    
    def __init__(self):
        self.config = {
            "word_url": "",  # word文件URL
            "word_file_desc": "",  # 文件描述
            "local_word_file_path": ""  # 本地文件路径
        }

    def parse_a_docx_table(self, some_doc_table: Table) -> str:
        """解析Word文档中的表格为Markdown格式"""
        res_list = []
        for i in range(len(some_doc_table.rows)):
            cur_row = some_doc_table.row_cells(i)
            cur_row_list = [x.text.replace('\n', '') for x in cur_row]
            res_list.append(cur_row_list)

        if len(res_list) > 2:
            markdown_str = "| " + " | ".join(res_list[0]) + " |\n"
            markdown_str += "| " + " | ".join("---" for _ in range(len(res_list[0]))) + " |\n"
            for res in res_list[1:]:
                markdown_str += "| " + " | ".join(cell for cell in res) + " |\n"
        elif len(res_list) == 2:
            if res_list[1] and len(set(res_list[1])) > 1 and res_list[1][0]:
                markdown_str = "| " + " | ".join(res_list[0]) + " |\n"
                markdown_str += "| " + " | ".join("---" for _ in range(len(res_list[0]))) + " |\n"
                for res in res_list[1:]:
                    markdown_str += "| " + " | ".join(cell for cell in res) + " |\n"
            else:
                markdown_str = "| " + " | ".join(res_list[0]) + " |\n"
        else:
            markdown_str = "| " + " | ".join(res_list[0]) + " |\n"
        return markdown_str

    def docx_iter_block_items(self, parent: Union[docx.Document, _Cell]):
        """遍历文档中的段落和表格"""
        if isinstance(parent, docx.Document):
            parent_elm = parent.element.body
        elif isinstance(parent, _Cell):
            parent_elm = parent._tc
        else:
            raise ValueError("不支持的父元素类型")

        for child in parent_elm.iterchildren():
            if isinstance(child, CT_P):
                yield Paragraph(child, parent)
            elif isinstance(child, CT_Tbl):
                yield Table(child, parent)

    def get_doc_content(self, file_path: str) -> List[str]:
        """获取文档内容"""
        doc = docx.Document(file_path)
        ele_list = self.docx_iter_block_items(doc)
        ele_result_list = []
        
        for ele in ele_list:
            try:
                if isinstance(ele, Table):
                    content = self.parse_a_docx_table(ele)
                else:
                    content = ele.text
                if content:
                    ele_result_list.append(content)
            except Exception as e:
                logger.error(f"处理文档元素时出错: {str(e)}")
                
        return ele_result_list

    def save_remote_file(self, file_name: str, unique_str: str) -> str:
        """保存远程文件到本地临时目录"""
        temp_dir = Path("temp")
        temp_dir.mkdir(exist_ok=True)
        return str(temp_dir / f"{unique_str}_{file_name}")

    def process_document(self, word_url: str = "", local_word_file_path: str = "") -> Dict:
        """处理Word文档并返回内容
        
        Args:
            word_url: Word文档的URL
            local_word_file_path: 本地Word文档路径
            
        Returns:
            Dict: 包含处理结果的字典
        """
        url = word_url if word_url.endswith('.docx') else local_word_file_path
        
        if url.startswith('http') and url.endswith('.docx'):
            try:
                response = requests.get(url)
                _, file_name = url.rsplit('/', 1)
                unique_str = f"{file_name.rsplit('.', 1)[0]}_{int(time.time())}"
                file_path = self.save_remote_file(file_name, unique_str)
                
                with open(file_path, 'wb') as f:
                    f.write(response.content)
                    
                ele_result_list = self.get_doc_content(file_path)
                return {"result": ele_result_list}
                
            except Exception as exc:
                logger.error(f"处理远程文档时出错: {str(exc)}")
                raise
                
        elif not url.startswith('http') and url.endswith('.docx'):
            try:
                ele_result_list = self.get_doc_content(url)
                return {"result": ele_result_list}
            except Exception as exc:
                logger.error(f"处理本地文档时出错: {str(exc)}")
                raise
                
        else:
            error_msg = "文件路径错误：请检查输入，需以.docx结尾"
            logger.error(error_msg)
            raise ValueError(error_msg)


# 使用示例
if __name__ == "__main__":
    processor = WordProcessor()
    # 处理本地文件
    result = processor.process_document(local_word_file_path="test.docx")
    print(json.dumps(result, ensure_ascii=False, indent=2))